let W = null
// 6.封装一个Watcher类
class Watcher {
  // 接受两个参数
  constructor(obj, key) {
    this.obj = obj
    this.key = key
    // 当我要获取值的时候,把当前的Wathcer保存在全局变量上
    W = this
    // 上来请求数据
    this.get()
    // 获取完值后将W改回去 用来dep判断 
    w = null
  }
  get() {
    //一旦这样拿值,就会直接进入当前key的响应式的getter函数中
    return this.obj[this.key]
  }
  //在watcher内部封装一个update方法,当通知我更新的时候,直接调用当前的update方法
  update() {
    console.log(
      "gogogo,有人通知我了,我重新去请求数据是:" +
      this.get() +
      ",然后开始重新渲染视图!!!!"
    );
  }
}

// 5.定义一个Dep类,用来收集依赖和通知更新
class Dep {
  constructor() {
    // 每次实例化Dep的时候,都会创建一个空的数组 用来保存所有的watcher
    this.subs = []
  }
  // 每次访问我的数据的时候,我就把watcher收集到dep.subs中
  depend(watcher) {
    this.subs.push(watcher);
    console.log(
      "每次访问我的数据的时候,我就把watcher收集到dep.subs这个数组中",
      this.subs
    );
  }
  // 每次更新的时候会执行
  notify() {
    console.log("通知所有的watcher 重新加载新的数据");
    this.subs.forEach((watcher) => {
      watcher.update();
    });
  }
}
// 4.封装defineReactive函数,主要是为了对单个数据做数据劫持 每次对一个值进行响应式的时候实例化一个Dep 
function defineReactive(_data, key, value) {
  // 上来把value值再次交给observe方法,进行深层次的数据响应式
  observe(value);
  // 每次对一个值进行响应式的时候,实例化一个Dep类
  const dep = new Dep()
  Object.defineProperty(_data, key, {
    get() {
      console.log("正在获取数据", "收集所有依赖性");
      // 如果在模板解析的时候 收集依赖性 将watcher放到dep创建的容器里
      if (W) {
        dep.depend(W)
      }
      return value
    },
    set(newValue) {
      if (newValue === value) return
      console.log('正在设置', "通知所有的watcher进行更新");
      value = newValue
      // 当我的响应式数据被修改的时候,我要通知所有的dep.subs里的watcher进行更新
      dep.notify()
    }
  })
}
// 3.封装一个Observer类 主要用来处理发布订阅模式的核心操作(响应式操作"收集依赖:通知更新)
class Observer {
  // 构造接受的是被要响应式正式处理的对象
  constructor(_data) {
    // 把这个对象放在实例上,将来在任何原型方法中都可以访问这个实例上的数据
    this._data = _data
    // 判断当前的数据是对象还是数组
    if (Array.isArray(_data)) {
      this.observeArray(_data);
    } else {
      this.walk(_data);
    }
  }
  // 在原型对象上封装一个对数组操作的方法
  observeArray(_data) {
    // 将对象再去observe重新开始监听
    _data.forEach(item => {
      observe(item)
    })
  }
  // 在原型对象上封装对象操作的方法
  walk(_data) {
    for (let key of Object.keys(_data)) {
      // 把每一个数据的响应式核心代码交给defineReactive函数来处理
      defineReactive(_data, key, _data[key]);
    }
  }
}

// 2.封装一个observe函数,接受_data的值,一个个的进行数据挟持
function observe(_data) {
  // 判断_data的类型
  if (typeof _data !== "object" || _data === null) return;
  // newObserver的实例并传参
  new Observer(_data)
}
// 1.封装一个Vue的构造函数 接受一个配置对象
function Vue(options) {
  //1.1将配置对象内的data放在实例悐this的_data属性上
  this._data = options.data
  // 1.2进行数据代理 把_data中的属性拿出来,一个个的放在vue实例上 把里面的每一项都放在Vue实例身上
  for (let key of Object.keys(this._data)) {
    Object.defineProperty(this, key, {
      get() {
        return this._data[key]
      },
      //接受新的值
      set(newVal) {
        this._data[key] = newVal
      }
    })
  }
  //拿到_data的数据开始进行响应式操作，我们首先把响应式操作的代码放在observe函数中
  observe(this._data);
}
// 实例化Vue
const vm = new Vue({
  data: {
    count: 0,
    Obj: { name: '罗飞', age: 18 },
    Ary: [
      { now: '晴天' },
      { today: "雨天" }
    ]
  }
})